import _ from 'lodash-es';

angular.module('portainer.app')
.factory('ResourceControlService', ['$q', 'ResourceControl', 'UserService', 'TeamService', 'ResourceControlHelper',
  function ResourceControlServiceFactory($q, ResourceControl, UserService, TeamService, ResourceControlHelper) {
  'use strict';
  const service = {};

  service.duplicateResourceControl = duplicateResourceControl;
  service.applyResourceControlChange = applyResourceControlChange;
  service.applyResourceControl = applyResourceControl;
  service.retrieveOwnershipDetails = retrieveOwnershipDetails;
  service.retrieveUserPermissionsOnResource =retrieveUserPermissionsOnResource;

  /**
   * PRIVATE SECTION
   */

  /**
   * Create a ResourceControl
   * @param {ResourceControlTypeString} rcType Type of ResourceControl
   * @param {string} rcID ID of involved resource
   * @param {ResourceControlOwnershipParameters} ownershipParameters Transcient type from view data to payload
   */
   function createResourceControl(rcType, resourceID, ownershipParameters) {
    var payload = {
      Type: rcType,
      Public: ownershipParameters.Public,
      AdministratorsOnly: ownershipParameters.AdministratorsOnly,
      ResourceID: resourceID,
      Users: ownershipParameters.Users,
      Teams: ownershipParameters.Teams,
      SubResourceIds: ownershipParameters.SubResourceIDs
    };
    return ResourceControl.create({}, payload).$promise;
  }

  /**
   * Update a ResourceControl
   * @param {String} rcID ID of involved resource
   * @param {ResourceControlOwnershipParameters} ownershipParameters Transcient type from view data to payload
   */
  function updateResourceControl(rcID, ownershipParameters) {
    const payload = {
      AdministratorsOnly: ownershipParameters.AdministratorsOnly,
      Public: ownershipParameters.Public,
      Users: ownershipParameters.Users,
      Teams: ownershipParameters.Teams
    };

    return ResourceControl.update({id: rcID}, payload).$promise;
  }

  /**
   * END PRIVATE SECTION
   */

  /**
   * PUBLIC SECTION
   */

   /**
    * Apply a ResourceControl after Resource creation
    * @param {int} userId ID of User performing the action
    * @param {AccessControlFormData} accessControlData ResourceControl to apply
    * @param {ResourceControlViewModel} resourceControl ResourceControl to update
    * @param {[]int} subResources SubResources managed by the ResourceControl
    */
  function applyResourceControl(userId, accessControlData, resourceControl, subResources=[]) {
    const ownershipParameters = ResourceControlHelper.RCFormDataToOwnershipParameters(userId, accessControlData, subResources);
    return updateResourceControl(resourceControl.Id, ownershipParameters);
  }

  /**
   * Duplicate an existing ResourceControl (default to AdministratorsOnly if undefined)
   * @param {int} userId ID of User performing the action
   * @param {ResourceControlViewModel} oldResourceControl ResourceControl to duplicate
   * @param {ResourceControlViewModel} newResourceControl ResourceControl to apply duplication to
   */
  function duplicateResourceControl(userId, oldResourceControl, newResourceControl) {
    const ownershipParameters = ResourceControlHelper.RCViewModelToOwnershipParameters(userId, oldResourceControl);
    return updateResourceControl(newResourceControl.Id, ownershipParameters);
  }

  /**
   * Update an existing ResourceControl or create a new one on existing resource without RC
   * @param {ResourceControlTypeString} rcType Type of ResourceControl
   * @param {String} resourceId ID of involved Resource
   * @param {ResourceControlViewModel} resourceControl Previous ResourceControl (can be undefined)
   * @param {AccessControlPanelData} formValues View data generated by AccessControlPanel
   */
  function applyResourceControlChange(rcType, resourceId, resourceControl, formValues) {
    const ownershipParameters = ResourceControlHelper.RCPanelDataToOwnershipParameters(formValues);
    if (resourceControl) {
      return updateResourceControl(resourceControl.Id, ownershipParameters);
    } else {
      return createResourceControl(rcType, resourceId, ownershipParameters);
    }
  }

  /**
   * Retrive users and team details for ResourceControlViewModel
   * @param {ResourceControlViewModel} resourceControl ResourceControl view model
   */
  function retrieveOwnershipDetails(resourceControl) {
    var deferred = $q.defer();

    if (!resourceControl) {
      deferred.resolve({ authorizedUsers: [], authorizedTeams: [] });
      return deferred.promise;
    }

    $q.all({
      users: resourceControl.UserAccesses.length > 0 ? UserService.users(false) : [],
      teams: resourceControl.TeamAccesses.length > 0 ? TeamService.teams() : []
    })
    .then(function success(data) {
      var authorizedUsers = ResourceControlHelper.retrieveAuthorizedUsers(resourceControl, data.users);
      var authorizedTeams = ResourceControlHelper.retrieveAuthorizedTeams(resourceControl, data.teams);
      deferred.resolve({ authorizedUsers: authorizedUsers, authorizedTeams: authorizedTeams });
    })
    .catch(function error(err) {
      deferred.reject({ msg: 'Unable to retrieve user and team information', err: err });
    });

    return deferred.promise;
  }

  function retrieveUserPermissionsOnResource(userID, isAdministrator, resourceControl) {
    var deferred = $q.defer();

    if (!resourceControl || isAdministrator) {
      deferred.resolve({ isPartOfRestrictedUsers: false, isLeaderOfAnyRestrictedTeams: false });
      return deferred.promise;
    }

    var found = _.find(resourceControl.UserAccesses, { UserId: userID });
    if (found) {
      deferred.resolve({ isPartOfRestrictedUsers: true, isLeaderOfAnyRestrictedTeams: false });
    } else {
      var isTeamLeader = false;
      UserService.userMemberships(userID)
      .then(function success(data) {
        var memberships = data;
        isTeamLeader = ResourceControlHelper.isLeaderOfAnyRestrictedTeams(memberships, resourceControl);
        deferred.resolve({ isPartOfRestrictedUsers: false, isLeaderOfAnyRestrictedTeams: isTeamLeader });
      })
      .catch(function error(err) {
        deferred.reject({ msg: 'Unable to retrieve user memberships', err: err });
      });
    }

    return deferred.promise;
  }

  /**
   * END PUBLIC SECTION
   */

  return service;
}]);
